class IUP_DATE_PICK
-- Creates a date editing interface element, which can displays a calendar for 
-- selecting a date.
--
-- In Windows is a native element. In GTK and Motif is a custom element. In 
-- Motif is not capable of displaying the calendar.

inherit
	IUP_WIDGET
		redefine
			execute_map,
			execute_unmap,
			execute_destroy,
			execute_getfocus,
			execute_killfocus,
			execute_enterwindow,
			execute_leavewindow,
			execute_k_any,
			execute_help,
			execute_valuechanged
		end
	IUP_WIDGET_INTERNALS
	IUP_WIDGET_CHILD
	IUP_WIDGET_NAME
	IUP_WIDGET_CUSTOM_ATTRIBUTES

create {ANY}
	date_pick

feature {ANY}

	date_pick
		-- Creates a month calendar interface element.
		local
			a_date_pick: POINTER
		do
			a_date_pick := int_date_pick
			set_widget(a_date_pick)
		end

	-- Attributes

	set_calendar_week_number (state: BOOLEAN)
		-- Shows the number of the week along the year. Default: False.
		do
			iup_open.set_attribute(Current, "CALENDARWEEKNUMBERS",
										  boolean_to_yesno(state))
		end

	set_format (value: STRING)
		-- [Windows Only]: Flexible format for the date in Windows. For more 
		-- information see "About Date and Time Picker Control" in the Windows 
		-- SDK: https://msdn.microsoft.com/EN-US/library/windows/desktop/bb761726(v=vs.85).aspx
		-- The Windows control was configured to display date only without any 
		-- time options. Default: "d'/'M'/'yyyy".
		--
		-- In Windows, when the user navigates to other pages in the calendar the 
		-- date is not changed until the user actually selects a day.
		--
		-- In Windows, FORMAT can have the following values, but other text in 
		-- the format string must be enclosed in single quotes:
		--
		-- Element 	    Description
		-- "d" 	       The one- or two-digit day. (default)
		-- "dd" 	       The two-digit day. Single-digit day values are 
		--              preceded by a zero.
		--              (Set when ZEROPRECED=True)
		-- "ddd" 	    The three-character weekday abbreviation.
		-- "dddd" 	    The full weekday name.
		-- "M" 	       The one- or two-digit month number. (default)
		-- "MM" 	       The two-digit month number. Single-digit values are 
		--              preceded by a zero.
		--              (Set when ZEROPRECED=True)
		-- "MMM"        The three-character month abbreviation.
		--              (Set when MONTHSHORTNAMES=True)
		-- "MMMM" 	    The full month name.
		-- "yy" 	       The last two digits of the year (that is, 1996 would be 
		--              displayed as "96"). (Not recommended)
		-- "yyyy" 	    The full year (that is, 1996 would be displayed as 
		--              "1996"). (default)
		do
			iup_open.set_attribute(Current, "FORMAT", value)
		end

	set_month_short_names (state: BOOLEAN)
		-- [Windows Only]: Month display will use a short name instead of 
		-- numbers. Must be set before ORDER. Default: False. Names will be in 
		-- the language of the system.
		do
			iup_open.set_attribute(Current, "MONTHSHORTNAMES",
										  boolean_to_yesno(state))
		end

	set_order (value: STRING)
		-- Day, month and year order. Can be any combination of "D", "M" and "Y" 
		-- without repetition, and with all three letters. It will set the FORMAT 
		-- attribute in Windows. It will NOT affect the VALUE attribute order. 
		-- Default: "DMY".
		require
			is_valid_order(value)
		do
			iup_open.set_attribute(Current, "ORDER", value)
		end

	set_separator (value: STRING)
		-- Separator between day, month and year. Must be set before ORDER in 
		-- Windows. Default: "/".
		do
			iup_open.set_attribute(Current, "SEPARATOR", value)
		end

	get_today: TUPLE[INTEGER, INTEGER, INTEGER]
		-- (read-only): Returns the date corresponding to today in format 
		-- [year, month, day]".
		do
			Result := convert_date_to_tuple(get_string_today)
		end

	get_string_today: STRING
 		-- (read-only): Returns the date corresponding to today in format 
		-- "year/month/day".
		do
			Result := iup_open.get_attribute(Current, "TODAY")
		end

	set_value (year, month, day: INTEGER)
		-- The current date
		require
			year >= 0
			month >= 1
			month <= 12
			day >= 1
			day <= 31
		local
			str: STRING
		do
			str := year.out
			str.append_string("/")
			str.append_string(month.out)
			str.append_string("/")
			str.append_string(day.out)
			
			iup_open.set_attribute(Current, "VALUE", str)
		end

	set_string_value (value: STRING)
		-- The current date always in the format "year/month/day"
		require
			valid_date: is_valid_string_date(value)
		do
			iup_open.set_attribute(Current, "VALUE", value)
		end

	open_dropdow
		-- Open the dropdown calendar (don't work on Windows).
		do
			iup_open.set_attribute(Current, "SHOWDROPDOWN", "YES")
		end

	close_dropdow
		-- Close the dropdown calendar.
		do
			iup_open.set_attribute(Current, "SHOWDROPDOWN", "NO")
		end

	set_today
		-- Set value to today date.
		do
			iup_open.set_attribute(Current, "VALUE", "TODAY")
		end

	get_value: TUPLE[INTEGER, INTEGER, INTEGER]
		-- Return the selected date in format [year, month, day]".
		do
			Result := convert_date_to_tuple(get_string_value)
		end

	get_string_value: STRING
		-- Return the selected date in format "year/month/day".
		do
			Result := iup_open.get_attribute(Current, "VALUE")
		end

	set_zero_preced (state: BOOLEAN)
		-- Day and month numbers will be preceded by a zero. Must be set before 
		-- ORDER in Windows. Default: False.
		do
			iup_open.set_attribute(Current, "ZEROPRECED",
										  boolean_to_yesno(state))
		end

	-- Callbacks

	-- Common
	set_cb_map (act: detachable FUNCTION[TUPLE[IUP_DATE_PICK], STRING])
		-- Called right after an element is mapped and its attributes updated.
		local
			operation: INTEGER
		do
			cb_map := act
			
			if cb_map /= Void then
				operation := 1
			else
				operation := 0
			end
						
			iup_open.set_callback (Current, "MAP_CB", "NONEEDED", operation)
		end

	set_cb_unmap (act: detachable FUNCTION[TUPLE[IUP_DATE_PICK], STRING])
		-- Called right before an element is unmapped.
		local
			operation: INTEGER
		do
			cb_unmap := act

			if cb_unmap /= Void then
				operation := 1
			else
				operation := 0
			end
			
			iup_open.set_callback (Current, "UNMAP_CB", "NONEEDED", operation)
		end

	set_cb_destroy (act: detachable FUNCTION[TUPLE[IUP_DATE_PICK], STRING])
		-- Called right before an element is destroyed.
		local
			operation: INTEGER
		do
			cb_destroy := act

			if cb_destroy /= Void then
				operation := 1
			else
				operation := 0
			end
			
			iup_open.set_callback (Current, "DESTROY_CB", "NONEEDED", operation)
		end

	set_cb_get_focus (act: detachable FUNCTION[TUPLE[IUP_DATE_PICK], STRING])
		-- Action generated when an element is given keyboard focus. 
		-- This callback is called after the KILLFOCUS_CB of the element 
		-- that loosed the focus. The {IUP}.get_focus function during the 
		-- callback returns the element that loosed the focus.
		local
			operation: INTEGER
		do
			cb_getfocus := act

			if cb_getfocus /= Void then
				operation := 1
			else
				operation := 0
			end
			
			iup_open.set_callback (Current, "GETFOCUS_CB", "NONEEDED", operation)
		end

	set_cb_kill_focus (act: detachable FUNCTION[TUPLE[IUP_DATE_PICK], STRING])
		-- Action generated when an element loses keyboard focus. This 
		-- callback is called before the GETFOCUS_CB of the element that 
		-- gets the focus.
		local
			operation: INTEGER
		do
			cb_killfocus := act

			if cb_killfocus /= Void then
				operation := 1
			else
				operation := 0
			end
			
			iup_open.set_callback (Current, "KILLFOCUS_CB", "NONEEDED", operation)
		end

	set_cb_enter_window (act: detachable FUNCTION[TUPLE[IUP_DATE_PICK], STRING])
		-- Action generated when the mouse enters the native element. 
		local
			operation: INTEGER
		do
			cb_enterwindow := act

			if cb_enterwindow /= Void then
				operation := 1
			else
				operation := 0
			end
			
			iup_open.set_callback (Current, "ENTERWINDOW_CB", "NONEEDED", operation)
		end

	set_cb_leave_window (act: detachable FUNCTION[TUPLE[IUP_DATE_PICK], STRING])
		-- Action generated when the mouse leaves the native element.
		local
			operation: INTEGER
		do
			cb_leavewindow := act

			if cb_leavewindow /= Void then
				operation := 1
			else
				operation := 0
			end
			
			iup_open.set_callback (Current, "LEAVEWINDOW_CB", "NONEEDED", operation)
		end

	set_cb_k_any (act: detachable FUNCTION[TUPLE[IUP_DATE_PICK, INTEGER], STRING])
		-- Action generated when a keyboard event occurs.
		-- IUP_WIDGET the element that activated the event.
		-- INTEGER identifier of typed key. Please refer to the Keyboard 
		-- Codes table for a list of possible values.
		--
		-- Returns: If IUP_IGNORE is returned the key is ignored and not
		-- processed by the control and not propagated. If returns 
		-- IUP_CONTINUE, the key will be processed and the event will be
		-- propagated to the parent of the element receiving it, this is 
		-- the default behavior. If returns IUP_DEFAULT the key is processed 
		-- but it is not propagated. IUP_CLOSE will be processed. 
		local
			operation: INTEGER
		do
			cb_k_any := act

			if cb_k_any /= Void then
				operation := 1
			else
				operation := 0
			end
			
			iup_open.set_callback (Current, "K_ANY", "NONEEDED", operation)
		end

	set_cb_help (act: detachable PROCEDURE[TUPLE[IUP_DATE_PICK]])
		-- Action generated when the user press F1 at a control. In Motif 
		-- is also activated by the Help button in some workstations 
		-- keyboard.
		-- Returns: IUP_CLOSE will be processed.
		local
			operation: INTEGER
		do
			cb_help := act

			if cb_help /= Void then
				operation := 1
			else
				operation := 0
			end
			
			iup_open.set_callback (Current, "HELP_CB", "NONEEDED", operation)
		end

	-- Extra
	set_cb_value_changed (act: detachable FUNCTION[TUPLE[IUP_DATE_PICK], STRING])
		local
			operation: INTEGER
		do
			cb_valuechanged := act

			if cb_valuechanged /= Void then
				operation := 1
			else
				operation := 0
			end
			
			iup_open.set_callback (Current, "VALUECHANGED_CB", "NONEEDED", operation)
		end

	-- Validations

	is_valid_order (value: STRING): BOOLEAN
		do

			if value.is_equal("DMY") or
				value.is_equal("DYM") or
				value.is_equal("MYD") or
				value.is_equal("MDY") or
				value.is_equal("YMD") or
				value.is_equal("YDM") then
				Result := True
			else
				Result := False
			end

		end

feature {IUP} -- Internal handle of callbacks

	-- Common callbacks
	
	execute_map: STRING
		do
			if attached cb_map as int_cb then
				Result := int_cb.item([Current])
			else
				Result := "IUP_DEFAULT"
			end
		end

	execute_unmap: STRING
		do
			if attached cb_unmap as int_cb then
				Result := int_cb.item([Current])
			else
				Result := "IUP_DEFAULT"
			end
		end

	execute_destroy: STRING
		do
			if attached cb_destroy as int_cb then
				Result := int_cb.item([Current])
			else
				Result := "IUP_DEFAULT"
			end
		end

	execute_getfocus: STRING
		do
			if attached cb_getfocus as int_cb then
				Result := int_cb.item([Current])
			else
				Result := "IUP_DEFAULT"
			end
		end

	execute_killfocus: STRING
		do
			if attached cb_killfocus as int_cb then
				Result := int_cb.item([Current])
			else
				Result := "IUP_DEFAULT"
			end
		end

	execute_enterwindow: STRING
		do
			if attached cb_enterwindow as int_cb then
				Result := int_cb.item([Current])
			else
				Result := "IUP_DEFAULT"
			end
		end

	execute_leavewindow: STRING
		do
			if attached cb_leavewindow as int_cb then
				Result := int_cb.item([Current])
			else
				Result := "IUP_DEFAULT"
			end
		end

	execute_k_any (c: INTEGER): STRING
		do
			if attached cb_k_any as int_cb then
				Result := int_cb.item([Current, c])
			else
				Result := "IUP_DEFAULT"
			end
		end

	execute_help
		do
			if attached cb_help as int_cb then
				int_cb.call([Current])
			end
		end

	-- Extra
	
	execute_valuechanged: STRING
		do
			if attached cb_valuechanged as int_cb then
				Result := int_cb.item([Current])
			else
				Result := "IUP_DEFAULT"
			end
		end
	
feature {NONE}

	-- For callbacks
	cb_map: detachable FUNCTION[TUPLE[IUP_DATE_PICK], STRING]
	cb_unmap: detachable FUNCTION[TUPLE[IUP_DATE_PICK], STRING]
	cb_destroy: detachable FUNCTION[TUPLE[IUP_DATE_PICK], STRING]
	cb_getfocus: detachable FUNCTION[TUPLE[IUP_DATE_PICK], STRING]
	cb_killfocus: detachable FUNCTION[TUPLE[IUP_DATE_PICK], STRING]
	cb_enterwindow: detachable FUNCTION[TUPLE[IUP_DATE_PICK], STRING]
	cb_leavewindow: detachable FUNCTION[TUPLE[IUP_DATE_PICK], STRING]
	cb_k_any: detachable FUNCTION[TUPLE[IUP_DATE_PICK, INTEGER], STRING]
	cb_help: detachable PROCEDURE[TUPLE[IUP_DATE_PICK]]

	cb_valuechanged: detachable FUNCTION[TUPLE[IUP_DATE_PICK], STRING]

	-- Internals
	
	int_date_pick: POINTER
		external
			"C inline use %"eiffel-iup.h%""
      alias
			"return IupDatePick();"
      end

end

-- The MIT License (MIT)

-- Copyright (c) 2016, 2017, 2019, 2020, 2021 by German A. Arias

-- Permission is hereby granted, free of charge, to any person obtaining a copy
-- of this software and associated documentation files (the "Software"), to deal
-- in the Software without restriction, including without limitation the rights
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-- copies of the Software, and to permit persons to whom the Software is
-- furnished to do so, subject to the following conditions:
--
-- The above copyright notice and this permission notice shall be included in 
-- all copies or substantial portions of the Software.
--
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-- SOFTWARE.

